/*
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

package bgtp;

import java.util.*;
import java.lang.*;
import java.lang.Thread;

/**
 *центральный мультиплексор протокола
 */
public class BGTPmm extends Thread
{
    private HashMap<Filter, MQInterface>	pool;
    private HashMap<String, BGTPWorker>		factory;
    private Vector<TreeMap<String, String>>	waitQueue;
    private HashMap<String, String>		conf;
    private boolean				mustRun;

    private static BGTPmm instance = null;

    /**
     *конструяктор 
     *непосредственно класс не должен конструироваться
     */
    private BGTPmm()
    {
	pool	  = new HashMap<Filter, MQInterface>();
	factory	  = new HashMap<String, BGTPWorker>();
	waitQueue = new Vector<TreeMap<String, String>>();
	conf	  = new HashMap<String, String>();
    }

    /**
     *получить экземпляр класса
     *@return экземпляр синглтона.
     */
    public static BGTPmm getInstance()
    {
	if(null == instance)
	    {
		instance = new BGTPmm();
	    }
	return instance;
    }

    /**
     *получить установленное значение параметра
     *@param key имя параметра значение которого надо получить
     *@return значение ранее установленного параметра
     */
    public String getValue(String key)
    {
	if(conf.containsKey(key) == true)
	    {
		return conf.get(key);
	    }
	return null;
    }

    /**
     *установить значение параметра
     *@param key имя параметра значение которого надо установить
     *@param value значение  параметра
     */
    public void setValue(String key, String value)
    {
	conf.put(key, value);
    }

    /**
     *зарегистрировать обработчик соединения
     *@param guid идентификатор ссоединения
     *@param wrk экземпляр обработчика. 
     */
    public void registerWorker(String guid, BGTPWorker wrk)
    {
	factory.put(guid, wrk);
	wrk.start();
    }
    
    /**
     *получить сужествующие подключения
     *@return набор имен подключений
     */
    public Set<String> getConnections()
    {
	return factory.keySet();
    }

    /**
     *проверка подключения на существование
     *@return результат проверки
     */
    public boolean workerExist(String guid)
    {
	return factory.containsKey(guid);
    }

    /**
     *отослать сообщение конкретному абоненту
     *@param guid идентификатор абонента
     *@param msg отсылаемое сообщение
     *@return возвращает true если сообщение удалось передать собработчику нужного абонента.
     */
    public boolean sendTo(String guid, TreeMap<String, String> msg)
    {
	if(factory.containsKey(guid) == false)
	    {
		return false;
	    }

	factory.get(guid).postMessage(msg);
	return true;
    }

    /**
     *отослать сообщение всем абонентам
     *@param msg отсылаемое сообщение
     */
    public void send(TreeMap<String, String> msg)
    {
	for(BGTPWorker slave: factory.values())
	    {
		slave.postMessage(msg);
	    }
    }

    /**
     *подписать очередь на получение сообщений прошедших фильтр
     *@param flt фильтр сообщений. прошедшие его попадут в очередь
     *@param mque очередь подписчица
     */
    public void subscribe(Filter flt, MQInterface mque)
    {
	pool.put(flt, mque);
    }
    /**
     *разослать сообщение в очереди
     *@param msg рассылаемое сообщение.
     */
    public void mail(TreeMap<String, String> msg)
    {
	synchronized(waitQueue)
	    {
		waitQueue.add(msg);
	    }
    }
    
    public void shutdown()
    {
	mustRun = false;
    }
    
    public void run()
    {
	Vector<TreeMap<String, String>>	tmpWaitQueue;
	mustRun	= true;

	while(true == mustRun)
	    {
		if(waitQueue.size() > 0)
		    {
			synchronized(waitQueue)
			    {
				tmpWaitQueue = waitQueue;
				waitQueue    = new Vector<TreeMap<String, String>>();
			    }

			for(TreeMap<String, String> item : tmpWaitQueue)
			    {
				for(Filter key: pool.keySet())
				    {
					if(key.check(item) == true)
					    {
						pool.get(key).add(item);
					    }
				    }
			    }
			tmpWaitQueue = null;
		    }
		else
		    {
			try
			    {
				Thread.sleep(100);
			    }
			catch(InterruptedException ie)
			    {
				ie.printStackTrace();
				break;
			    }
		    }

		//приберем трупики на всякий случай
		try
		    {
			for(String key : factory.keySet())
			    {
				if(factory.get(key).getState() == Thread.State.TERMINATED)
				    {
					factory.get(key).join();
					factory.remove(key);
				    }
			    }
		catch(InterruptedException ie)
		    {
			ie.printStackTrace();
		    }
	    }
	
	for(String key: factory.keySet())
	    {
		factory.get(key).shutdown();
		factory.get(key).join();
	    }
    }
}